package com.easyspringboot.bulider;

import com.easyspringboot.bean.Constants;
import com.easyspringboot.bean.FieldInfo;
import com.easyspringboot.bean.TableInfo;
import com.easyspringboot.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.List;
import java.util.Map;

/**
 * @Author xiaoyi
 * @Version 1.0
 * @ClassName BuildMapperXML
 * @Time 23:22
 */
public class BuildMapperXML {

    private static final Logger logger = LoggerFactory.getLogger(BuildMapper.class);
    private static final String BASE_COLUMN_LIST = "base_column_list";
    private static final String BASE_QUERY_CONDITION = "base_query_condition";
    private static final String BASE_QUERY_CONDITION_EXTEND = "base_query_condition_extend";

    private static final String QUERY_CONDITION = "query_condition";

    public static void execute(TableInfo tableInfo) {
        File folder = new File(Constants.PATH_MAPPER_XML);
        if (!folder.exists()) {
            folder.mkdirs();
        }
        String className = tableInfo.getBeanName() + Constants.SUFFIX_BEAN_MAPPERS;

        File poFile = new File(folder, className + ".xml");

        OutputStream out = null;
        OutputStreamWriter outw = null;
        BufferedWriter bw = null;
        try {
            out = new FileOutputStream(poFile);
            outw = new OutputStreamWriter(out, "utf8");
            bw = new BufferedWriter(outw);
            bw.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
            bw.newLine();
            bw.write("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"");
            bw.newLine();
            bw.write("        \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">");
            bw.newLine();
            bw.write("<mapper namespace=\"" + Constants.PACKAGE_MAPPER + "." + className + "\">");
            bw.newLine();
            bw.write("\t<!--实体映射-->");
            bw.newLine();
            String poClass = Constants.PACKAGE_PO + "." + tableInfo.getBeanName();//名字
            bw.write("\t<resultMap id=\"base_result_map\" type=\"" + poClass + "\">");
            bw.newLine();
            FieldInfo idField = null;//主键字段信息
            Map<String, List<FieldInfo>> keyIndexMap = tableInfo.getKeyIndexMap();
            for (Map.Entry<String, List<FieldInfo>> entry : keyIndexMap.entrySet()) {
                if ("PRIMARY".equals(entry.getKey())) {
                    List<FieldInfo> keyFieldInfos = entry.getValue();//主键字段信息
                    idField = keyFieldInfos.get(0);
                    logger.info("主键字段信息测试:{}", idField);
                    break;
                }
            }
            for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
                bw.write("\t\t<!--" + fieldInfo.getComment() + "-->");
                bw.newLine();
                String key = "";

                if (idField != null && fieldInfo.getPropertyName().equals(idField.getPropertyName())) {
                    key = "id";
                } else {
                    key = "result";
                }
                bw.write("\t\t<" + key + " column=\"" + fieldInfo.getFiledName() + "\" property=\"" + fieldInfo.getPropertyName() + "\"/>");
                bw.newLine();

            }
            bw.write("\t</resultMap>");
            bw.newLine();
            //通用查询条件
            bw.write("\t<!--通用查询结果列-->");
            bw.newLine();

            bw.write("\t<sql id=\"" + BASE_COLUMN_LIST + "\">");
            bw.newLine();
            StringBuilder columnBuilder = new StringBuilder();
            for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
                columnBuilder.append(fieldInfo.getFiledName()).append(",");
            }
            String columnBuilderStr = columnBuilder.substring(0, columnBuilder.lastIndexOf(","));
            bw.write("\t" + columnBuilderStr);
            bw.newLine();
            bw.write("\t</sql>");
            bw.newLine();
            bw.write("\t<!--基础查询结果列-->");
            bw.newLine();
            bw.write("\t<sql id=\"" + BASE_QUERY_CONDITION + "\">");

            for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
                String stringQuery = "";
                if (ArrayUtils.contains(Constants.SQL_STRING_TYPES, fieldInfo.getSqlType())) {
                    stringQuery = "and query." + fieldInfo.getPropertyName() + "!=''";
                }
                bw.newLine();
                bw.write("\t\t<if test=\"query." + fieldInfo.getPropertyName() + " != null " + stringQuery + "\">");
                bw.newLine();
                bw.write("\t\t\tand id = #{query." + fieldInfo.getPropertyName() + "}");
                bw.newLine();
                bw.write("\t\t</if>");
            }
            bw.newLine();
            bw.write("\t</sql>");
            bw.newLine();
            bw.write("\t<!--扩展查询条件-->");
            bw.newLine();
            bw.write("\t<sql id=\"" + BASE_QUERY_CONDITION_EXTEND + "\">");

            for (FieldInfo fieldInfo : tableInfo.getFieldExtendList()) {
                String andWhere = "";
                if (ArrayUtils.contains(Constants.SQL_STRING_TYPES, fieldInfo.getSqlType())) {
                    andWhere = "and " + fieldInfo.getFiledName() + " like CONCAT('%',#{query." + fieldInfo.getPropertyName() + "},'%')";
                } else if (ArrayUtils.contains(Constants.SQL_DATE_TYPES, fieldInfo.getSqlType()) || ArrayUtils.contains(Constants.SQL_DATA_TIME_TYPES, fieldInfo.getSqlType())) {
                    if (fieldInfo.getPropertyName().endsWith(Constants.SUFFIX_BEAN_QUERY_TIME_START)) {
                        andWhere = "<![CDATA[ and " + fieldInfo.getFiledName() + " >=str_to_date(#{query." + fieldInfo.getPropertyName() + "},'%Y-%m-%d') ]]>";

                    } else if (fieldInfo.getPropertyName().endsWith(Constants.SUFFIX_BEAN_QUERY_TIME_END)) {
                        andWhere = "<![CDATA[ and " + fieldInfo.getFiledName() + " <date_sub(str_to_date(#{query." + fieldInfo.getPropertyName() + "},'%Y-%m-%d')," +
                                "interval -1 day) ]]>";

                    }
                }
                bw.newLine();
                bw.write("\t\t<if test=\"query." + fieldInfo.getPropertyName() + " != null and query." + fieldInfo.getPropertyName() + " != ''" + "\">");
                bw.newLine();
                bw.write("\t\t\t" + andWhere);
                bw.newLine();

                bw.write("\t\t</if>");
            }
            bw.newLine();
            bw.write("\t</sql>");
            bw.newLine();
            bw.write("\t<!--通用查询条件-->");
            bw.newLine();
            bw.write("\t<sql id=\"" + QUERY_CONDITION + "\">");
            bw.newLine();
            bw.write("\t\t<where>");
            bw.newLine();
            bw.write("\t\t\t<include refid=\"" + BASE_QUERY_CONDITION + "\"/>");
            bw.newLine();
            bw.write("\t\t\t<include refid=\"" + BASE_QUERY_CONDITION_EXTEND + "\"/>");
            bw.newLine();
            bw.write("\t\t</where>");
            bw.newLine();
            bw.write("\t</sql>");
            bw.newLine();

            //查询列表
            bw.write("\t<!--查询列表-->");
            bw.newLine();
            bw.write("\t<select id=\"selectList\" resultMap=\"base_result_map\">");
            bw.newLine();
            bw.write("\t\tselect <include refid=\"" + BASE_COLUMN_LIST + "\"/> from " + tableInfo.getTableName() + "<include refid=\"" + QUERY_CONDITION + "\"/>");
            bw.newLine();
            bw.write("\t\t<if test=\"query.orderBy != null\"> order by ${query.orderBy} </if>");
            bw.newLine();
            bw.write("\t\t<if test=\"query.simplePage != null\">limit #{query.simplePage.start},#{query.simplePage.end}</if>");
            bw.newLine();
            bw.write("\t</select>");
            bw.newLine();
            //查询数量
            bw.write("\t<!--查询数量-->");
            bw.newLine();
            bw.write("\t<select id=\"selectCount\" resultType=\"java.lang.Long\">");
            bw.newLine();
            bw.write("\t\tselect count(1) from " + tableInfo.getTableName() + " <include refid=\"" + QUERY_CONDITION + "\"/>");
            bw.newLine();
            bw.write("\t</select>");
            bw.newLine();

            //查询单个
            bw.write("\t<!--单个插入（匹配有值字段）-->");
            bw.newLine();
            bw.write("\t<insert id=\"insert\" parameterType=\"" + poClass + "\">");
            bw.newLine();
            FieldInfo autoIncrementField = null;
            for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
                if (fieldInfo.isAutoIncrement()) {
                    autoIncrementField = fieldInfo;
                    break;
                }
            }
            if (autoIncrementField != null) {
                bw.write("\t\t<selectKey keyProperty=\"bean." + autoIncrementField.getPropertyName() + "\" resultType=\"" + autoIncrementField.getJavaType() + "\" order=\"AFTER\">");
                bw.newLine();
                bw.write("\t\t\tselect LAST_INSERT_ID()");
                bw.newLine();
                bw.write("\t\t</selectKey>");
                bw.newLine();
            }
            bw.newLine();
            bw.write("\t\tINSERT INTO " + tableInfo.getTableName());
            bw.newLine();
            bw.write("\t\t<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
            bw.newLine();
            for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
                if (fieldInfo.isAutoIncrement()) {
                    continue;
                }
                bw.write("\t\t\t<if test=\"bean." + fieldInfo.getPropertyName() + " != null\">");
                bw.newLine();
                bw.write("\t\t\t\t" + fieldInfo.getFiledName() + ",");
                bw.newLine();
                bw.write("\t\t\t</if>");
                bw.newLine();
            }

            bw.write("\t\t</trim>");
            bw.newLine();
            bw.write("\t\t<trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">");
            bw.newLine();
            for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
                if (fieldInfo.isAutoIncrement()) {
                    continue;
                }
                bw.write("\t\t\t<if test=\"bean." + fieldInfo.getPropertyName() + " != null\">");
                bw.newLine();
                bw.write("\t\t\t\t" + "#{bean." + fieldInfo.getPropertyName() + "},");
                bw.newLine();
                bw.write("\t\t\t</if>");
                bw.newLine();
            }
            bw.write("\t\t</trim>");
            bw.newLine();
            bw.write("\t</insert>");
            bw.newLine();

            //插入或更新
            bw.write("\t<!--插入或更新（匹配有值字段）-->");
            bw.newLine();
            bw.write("\t<insert id=\"updateOrInsert\" parameterType=\"" + poClass + "\">");
            bw.newLine();
            bw.write("\t\tINSERT INTO " + tableInfo.getTableName());
            bw.newLine();
            bw.write("\t\t<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
            bw.newLine();
            for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
                if (fieldInfo.isAutoIncrement()) {
                    continue;
                }
                bw.write("\t\t\t<if test=\"bean." + fieldInfo.getPropertyName() + " != null\">");
                bw.newLine();
                bw.write("\t\t\t\t" + fieldInfo.getFiledName() + ",");
                bw.newLine();
                bw.write("\t\t\t</if>");
                bw.newLine();
            }

            bw.write("\t\t</trim>");
            bw.newLine();
            bw.write("\t\t<trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">");
            bw.newLine();
            for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
                if (fieldInfo.isAutoIncrement()) {
                    continue;
                }
                bw.write("\t\t\t<if test=\"bean." + fieldInfo.getPropertyName() + " != null\">");
                bw.newLine();
                bw.write("\t\t\t\t" + "#{bean." + fieldInfo.getPropertyName() + "},");
                bw.newLine();
                bw.write("\t\t\t</if>");
                bw.newLine();
            }
            bw.write("\t\t</trim>");
            bw.newLine();
            bw.write("\t\tON DUPLICATE KEY UPDATE ");
            bw.newLine();
            bw.write("\t\t<trim prefix=\"\" suffix=\"\" suffixOverrides=\",\">");
            bw.newLine();
            for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
                if (fieldInfo.isAutoIncrement()) {
                    continue;
                }
                bw.write("\t\t\t<if test=\"bean." + fieldInfo.getPropertyName() + " != null\">");
                bw.newLine();
                bw.write("\t\t\t\t" + fieldInfo.getFiledName() + "= VALUES (" + fieldInfo.getFiledName() + "),");
                bw.newLine();
                bw.write("\t\t\t</if>");
                bw.newLine();
            }
            bw.write("\t\t</trim>");
            bw.newLine();
            bw.write("\t</insert>");
            bw.newLine();
            //批量插入
            bw.write("\t<!--批量插入-->");
            bw.newLine();
            bw.write("\t<insert id=\"insertBatch\" parameterType=\"" + poClass + "\"" + " useGeneratedKeys=\"true\" keyProperty = \"id\"" + ">");
            bw.newLine();
            bw.write("\t\tINSERT INTO " + tableInfo.getTableName());
            bw.write("(");
            StringBuffer insertSb = new StringBuffer();
            StringBuffer foreachSb = new StringBuffer();
            for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
                if (fieldInfo.isAutoIncrement()) {
                    continue;
                }
                insertSb.append(fieldInfo.getFiledName() + ",");
                foreachSb.append("#{item." + fieldInfo.getPropertyName() + "},");
            }
            String insertSbStr = insertSb.substring(0, insertSb.length() - 1);
            String foreachSbStr = foreachSb.substring(0, foreachSb.length() - 1);
            bw.write(insertSbStr);
            bw.write(") VALUES ");
            bw.newLine();
            bw.write("\t\t<foreach collection=\"list\" item=\"item\"  separator=\",\" >");
            bw.newLine();
            bw.write("\t\t\t(");
            bw.write(foreachSbStr + ")");
            bw.newLine();
            bw.write("\t\t</foreach>");
            bw.newLine();
            bw.write("\t</insert>");
            bw.newLine();

            //批量插入或更新
            bw.write("\t<!--批量插入或更新-->");
            bw.newLine();
            bw.write("\t<insert id=\"insertBatchOrUpdate\" parameterType=\"" + poClass + "\"" + " useGeneratedKeys=\"true\" keyProperty = \"id\"" + ">");
            bw.newLine();
            bw.write("\t\tINSERT INTO " + tableInfo.getTableName());
            bw.write("(");
            for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
                if (fieldInfo.isAutoIncrement()) {
                    continue;
                }
                insertSb.append(fieldInfo.getFiledName() + ",");
                foreachSb.append("#{item." + fieldInfo.getPropertyName() + "},");
            }
            insertSbStr = insertSb.substring(0, insertSb.length() - 1);
            foreachSbStr = foreachSb.substring(0, foreachSb.length() - 1);
            bw.write(insertSbStr);
            bw.write(") VALUES ");
            bw.newLine();
            bw.write("\t\t<foreach collection=\"list\" item=\"item\"  separator=\",\" >");
            bw.newLine();
            bw.write("\t\t\t(");
            bw.write(foreachSbStr + ")");
            bw.newLine();
            bw.write("\t\t</foreach>");
            bw.newLine();
            bw.write("\t\tON DUPLICATE KEY UPDATE ");
            StringBuffer updateSb = new StringBuffer();

            for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
                if (fieldInfo.isAutoIncrement()) {
                    continue;
                }
                updateSb.append("\n\t\t" + fieldInfo.getFiledName() + " = VALUES (" + fieldInfo.getFiledName() + "),");

            }
            String updateSbStr = updateSb.substring(0, updateSb.length() - 1);
            bw.write("\t\t" + updateSbStr);
            bw.newLine();
            bw.write("\t</insert>");
            bw.newLine();
            bw.newLine();


            //根据属性更新，删除，查询

            for (Map.Entry<String, List<FieldInfo>> entry : keyIndexMap.entrySet()) {
                List<FieldInfo> keyFieldInfos = entry.getValue();//主键字段信息
                StringBuffer paramSb = new StringBuffer();//参数
                String methodNameStr = null;//方法名
                String paramSbStr = null;//参数
                Integer index = 0;
                StringBuffer methodName = new StringBuffer();//方法名
                for (FieldInfo fieldInfo : keyFieldInfos) {
                    methodName.append(StringUtils.uperCaseFirstLetter(fieldInfo.getPropertyName()));
                    paramSb.append(fieldInfo.getFiledName() + "=#{" + fieldInfo.getPropertyName() + "}");
                    if (index < keyFieldInfos.size() - 1) {
                        methodName.append("And");
                        paramSb.append(" AND ");
                        methodNameStr = methodName.substring(0, methodName.length() - 3);
                        paramSbStr = paramSb.substring(0, paramSb.length() - 5);

                    } else {
                        methodNameStr = methodName.toString();
                        paramSbStr = paramSb.toString();
                    }


                }

                //根据属性更新
                bw.write("\t<!--根据" + methodNameStr + "更新-->");
                bw.newLine();
                bw.write("\t<update id=\"updateBy" + methodNameStr + "\" parameterType=\"" + poClass + "\">");
                bw.newLine();
                bw.write("\t\tUPDATE " + tableInfo.getTableName());
                bw.newLine();
                bw.write("\t\t<set>");
                bw.newLine();
                for (FieldInfo fieldInfo : tableInfo.getFieldList()) {
                    if (fieldInfo.isAutoIncrement()) {
                        continue;
                    }
                    bw.write("\t\t\t<if test=\"bean." + fieldInfo.getFiledName() + " != null\">");
                    bw.newLine();
                    bw.write("\t\t\t\t" + fieldInfo.getFiledName() + "=#{ bean." + fieldInfo.getPropertyName() + "},");
                    bw.newLine();
                    bw.write("\t\t\t</if>");
                    bw.newLine();
                }
                bw.write("\t\t</set>");
                bw.newLine();
                bw.write("\t\tWHERE " + paramSbStr);
                bw.newLine();
                bw.write("\t</update>");
                bw.newLine();

                //根据属性删除
                bw.write("\t<!--根据" + methodNameStr + "删除-->");
                bw.newLine();
                bw.write("\t<delete id=\"deleteBy" + methodNameStr + "\">");
                bw.newLine();
                bw.write("\t\tDELETE FROM " + tableInfo.getTableName() + " WHERE " + paramSbStr);
                bw.newLine();
                bw.write("\t</delete>");
                bw.newLine();
                //根据属性查询
                bw.write("\t<!--根据" + methodNameStr + "查询-->");
                bw.newLine();
                bw.write("\t<select id=\"selectBy" + methodNameStr +  "\" resultMap=\"base_result_map\">");
                bw.newLine();
                bw.write("\t\tselect <include refid=\"" + BASE_COLUMN_LIST + "\" /> from " + tableInfo.getTableName() + " where  " + paramSbStr);
                bw.newLine();
                bw.write("\t</select>");
                bw.newLine();
                bw.newLine();
            }


            bw.newLine();
            bw.write("</mapper>");
            bw.flush();
        } catch (Exception e) {
            logger.error("创建mapper_xml失败", e);
        } finally {
            if (bw != null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outw != null) {
                try {
                    outw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }


    }
}